<?php

namespace StudyBuddy\I18n;

/**
 * Class for parsing XLIFF XML file
 * and converting the file to array
 * and setting that array into $this->aMessages
 *
 */
class XliffCatalog implements \StudyBuddy\Interfaces\Translator, \Serializable {

    /**
     * Name of locate for which
     * the messages are translated
     *
     * @var string
     */
    protected $locale = null;

    /**
     * Array of messages
     * keys are strings to be translated
     * values are translated values
     *
     * @var array
     */
    protected $aMessages = array();

    /**
     * 
     * Constructor
     * @param string $file
     * @param string $locale
     */
    public function __construct($file, $locale) {
        //echo "\n".__METHOD__.' xliff file: '.$file;
        $this->locale = $locale;
        d('$this->locale: ' . $this->locale);

        /**
         * If File $file does not exist or not
         * readable there will not be any errors
         * Simply the loading of file will be skipped and
         * the object will have only the default empty
         * array of $this->aMessages;
         */
        if (is_readable($file)) {
            //echo ' '.__METHOD__.' file is readable ';
            $this->parseFile($file);
        } else {
            //echo ' '.__METHOD__.' file '.$file.' is not readable ';
            d('XLIFF file ' . $file . ' does not exist or not readable');
        }
    }

    /**
     * Load Xliff File, validate it
     * and create DOMDocument object from it
     *
     * @param string $file full path to XLIFF xml file
     * @throws \StudyBuddy\DevException
     * @throws \Exception
     */
    protected function parseFile($file) {
        d('parsing $file: ' . $file);
        $oDom = new \DOMDocument();
        $current = libxml_use_internal_errors(true);
        if (!@$oDom->load($file, LIBXML_COMPACT)) {
            $err = implode("\n", $this->getXmlErrors());
            exit($err);
            throw new \StudyBuddy\DevException($err);
        }

        $location = \str_replace('\\', '/', __DIR__) . '/schema/xml.xsd';
        d('$location: ' . $location);

        $parts = explode('/', $location);

        $drive = '\\' === DIRECTORY_SEPARATOR ? array_shift($parts) . '/' : '';
        $location = 'file:///' . $drive . implode('/', array_map('rawurlencode', $parts));
        d('$location: ' . $location);

        $source = \file_get_contents(__DIR__ . '/schema/xliff-core-1.2-strict.xsd');
        $source = \str_replace('http://www.w3.org/2001/xml.xsd', $location, $source);

        if (!@$oDom->schemaValidateSource($source)) {
            d('schemaValidateSource() failed');

            throw new \StudyBuddy\DevException(implode("\n", $this->getXmlErrors()));
        }

        $oDom->validateOnParse = true;
        $oDom->normalizeDocument();
        d('cp');
        libxml_use_internal_errors($current);

        $this->xml2array($oDom);
    }

    /**
     * Parsed the DOMDocument that holds
     * XLIFF XML file
     * and creates array of messages in
     * string => value format
     * and then sets it as
     * $this->aMessages array
     * 
     * @param \DOMDocument $o
     */
    protected function xml2array(\DOMDocument $o) {

        $xp = new \DOMXPath($o);
        $xp->registerNamespace('xliff', 'urn:oasis:names:tc:xliff:document:1.2');
        $elements = $xp->query('//xliff:trans-unit');

        foreach ($elements as $element) {
            $s = $element->getElementsByTagName('source')->item(0)->nodeValue;
            $v = $element->getElementsByTagName('target')->item(0)->nodeValue;
            $this->aMessages[$s] = $v;
        }

        return $this;
    }

    /**
     * Returns the XML errors of the internal XML parser
     *
     * @author Symfony project
     *
     * @return array  An array of errors
     */
    protected function getXmlErrors() {
        $errors = array();
        foreach (libxml_get_errors() as $error) {
            $errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)', LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR', $error->code, trim($error->message), $error->file ? $error->file : 'n/a', $error->line, $error->column
            );
        }

        \libxml_clear_errors();
        \libxml_use_internal_errors(false);

        return $errors;
    }

    /**
     * (non-PHPdoc)
     * @see StudyBuddy\Interfaces.Translator::getMessages()
     */
    public function getMessages() {
        return $this->aMessages;
    }

    /**
     * (non-PHPdoc)
     * @see StudyBuddy\Interfaces.Translator::getLocale()
     */
    public function getLocale() {
        return $this->locale;
    }

    /**
     * (non-PHPdoc)
     * @see StudyBuddy\Interfaces.Translator::get()
     */
    public function get($string, array $vars = null, $default = null) {
        $str = (!empty($this->aMessages[$string])) ? $this->aMessages[$string] : (is_string($default) ? $default : $string);

        return (null === $vars) ? $str : strtr($str, $vars);
    }

    /**
     * (non-PHPdoc)
     * @see StudyBuddy\Interfaces.Translator::has()
     */
    public function has($string) {
        return array_key_exists($string, $this->aMessages);
    }

    /**
     * (non-PHPdoc)
     * @see Serializable::serialize()
     */
    public function serialize() {
        return \json_encode(array('messages' => $this->aMessages, 'locale' => $this->locale));
    }

    /**
     * (non-PHPdoc)
     * @see Serializable::unserialize()
     */
    public function unserialize($serialized) {
        $a = \json_decode($serialized, true);
        $this->aMessages = $a['messages'];
        $this->locale = $a['locale'];
    }

}
